C#: Preserve NullSafe marker through CSharpTemplate.Rewrite()#7140
Merged
knutwannheden merged 2 commits intomainfrom Mar 26, 2026
Merged
C#: Preserve NullSafe marker through CSharpTemplate.Rewrite()#7140knutwannheden merged 2 commits intomainfrom
NullSafe marker through CSharpTemplate.Rewrite()#7140knutwannheden merged 2 commits intomainfrom
Conversation
Move the NullSafe marker from the Name Identifier to the MethodInvocation/FieldAccess node where it semantically belongs, and teach the pattern matcher and template engine to preserve it through rewrites. - Parser: place NullSafe on MI/FA Markers instead of Name Markers - Printer: check mi.Markers instead of mi.Name.Markers - Pattern matching: asymmetric NullSafe check — pattern without ?. matches both . and ?. access, but pattern with ?. only matches ?. - Template substitution: when a capture was the select of a NullSafe MI/FA, transfer the marker to the template output MI/FA
NullSafe marker through CSharpTemplate.Rewrite()
… preservation Move the NullSafe marker for ?[ from ArrayDimension.Markers to ArrayAccess.Markers for consistency with MethodInvocation and FieldAccess. Split the combined space into Dimension prefix (space before ?) and NullSafe.DotPrefix (space between ? and [). Add NullSafe recording and transfer for ArrayAccess in the pattern matcher and template engine.
2e6e4ae to
86736ab
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
When
CSharpTemplate.Rewrite()transforms a chained method call that has a?.(NullSafe marker) on an intermediate call, the marker was lost in the output because the template builds a fresh tree. For example,dict["key"]?.Where(x => x > 0).First()rewritten todict["key"]?.First(x => x > 0)would lose the?., producingdict["key"].First(x => x > 0)instead.Two root causes: (1) the NullSafe marker lived on the Name Identifier rather than the MethodInvocation/FieldAccess node, making it invisible to structural comparison and unintuitive for recipe authors; (2) the pattern matcher and template engine had no mechanism to preserve it.
Summary
mi.Name.Markerstomi.Markers(andfa.Markersfor FieldAccess) across parser and printer — this is the semantically correct location since?.describes the access operation, not the name?.matches both.and?.access (lenient), but a pattern with?.only matches?.(strict). This avoids forcing recipe authors to duplicate patternsTest plan
PreservesNullConditionalInRewrite—{x}.Where({pred}).First()→{x}.First({pred})preserves?.PreservesNullConditionalOnFieldAccess—{x}.Length→{x}.Countpreserves?.NullConditionalNotAddedWhenOriginalHasNone— no spurious?.addedRegularDotPatternMatchesNullConditionalAccess— pattern without?.now matches?.NullConditionalPatternDoesNotMatchRegularDotAccess— pattern with?.still rejects.